package com.teamblueplus.comicmaker;

import java.util.ArrayList;
import java.util.Collections;

import com.teamblueplus.R;
import com.teamblueplus.brush.PaintBrush;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.EditText;

/**
 * ComicSurfaceView contains the surfaceview canvas for images and drawing to be
 * added.
 * 
 * @author qui
 * 
 */
public class ComicSurfaceView extends SurfaceView implements
		SurfaceHolder.Callback {

	String TAG = "mySurfaceView"; // tag for logging
	private ComicDrawThread myThread;
	private Context mContext;
	boolean surfaceCreated = false;
	boolean isResize = false;
	private Resources mRes;
	private boolean isSurfaceDestroyed = false;

	// for touching events
	int touchX, touchY;
	float initialDistance, currentDistance;
	float distX, distY;


	ArrayList<DrawingLayer> drawingLayerArray;

	// brush functionality
	private int isBrushOn;
	public PaintBrush brush2;

	/**
	 * Constructor for ComicSurfaceView, initializes new drawing thread
	 * @param context
	 * @param attrs
	 */
	public ComicSurfaceView(Context context, AttributeSet attrs) {
		super(context, attrs);
		getHolder().addCallback(this);
		mContext = context;
		mRes = context.getResources();
		drawingLayerArray = new ArrayList<DrawingLayer>();
		myThread = new ComicDrawThread(getHolder(), mContext, drawingLayerArray);

		setFocusable(true);

		// brush functionality
		// brush is disable at first
		isBrushOn = 0;
		brush2 = new PaintBrush();

	}


	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {

		// this line is important, else brush will not be draw
		// I don't know whats the reason, need to find out
		Log.d("###", "surfaceChange + mBitmap created");
		if (myThread.mBitmap == null){
		myThread.mBitmap = Bitmap.createBitmap(width, height,
				Bitmap.Config.ARGB_8888);
		}

	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		if (myThread.getState() == Thread.State.TERMINATED) {
			myThread = new ComicDrawThread(getHolder(), mContext,
					drawingLayerArray);
		}

		// set the brush array to thread
		myThread.setBrushArray(brush2);
		
		myThread.setRunning(true);
		myThread.start();

		isSurfaceDestroyed = false;
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		// tell the thread to stop and waits for it to join
		myThread.setRunning(false);
		
		myThread.cleanup();

		boolean retry = true;
		
		isSurfaceDestroyed = true;
		while (retry) {
			try {
				myThread.join();
				retry = false;
			} catch (InterruptedException e) {
				// we will try it again and again...
			}
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {

		// call brush event handler
		paintBrush2(event);

		switch (event.getAction() & MotionEvent.ACTION_MASK) {

		case MotionEvent.ACTION_DOWN:
			touchX = (int) event.getX();
			touchY = (int) event.getY();

			// get the layer, save the touch
			myThread.handleActionDown(touchX, touchY);

			break;

		case MotionEvent.ACTION_POINTER_DOWN:
			Log.d("finger down", "pinch zoom");
			if (myThread.isTouched()) {
				isResize = true;
				distX = event.getX(0) - event.getX(1);
				distY = event.getY(0) - event.getY(1);
				initialDistance = FloatMath.sqrt(distX * distX + distY * distY);
			}
			break;

		// end 2 fingers
		case MotionEvent.ACTION_POINTER_UP:
			if (myThread.isTouched()) {
				myThread.setTouched(false);
			}
			isResize = false;
			myThread.setNewLayerSize();
			break;

		case MotionEvent.ACTION_MOVE:
			// the gestures

			if (isBrushOn == 0 && myThread.isTouched()) {
				// get the selected layer index
				int selectedLayer = myThread.getSelectedLayer();
				int dragX = (int) event.getX();
				int dragY = (int) event.getY();

				// resizing
				if (isResize) {
					// Get the current distance
					distX = event.getX(0) - event.getX(1);
					distY = event.getY(0) - event.getY(1);
					currentDistance = FloatMath.sqrt(distX * distX + distY
							* distY);

					myThread.resizeLayer(initialDistance, currentDistance);

					// the layer was picked up and is being dragged
				} else {

					int xAmount = dragX - touchX;
					int yAmount = dragY - touchY;

					this.moveLayerBy(selectedLayer, xAmount, yAmount);
				}
			}
			break;

		case MotionEvent.ACTION_UP:
			// touch was released
			if (myThread.isTouched()) {
				myThread.setTouched(false);
			}
			break;
		}

		return true;
	}

	// brush functionality
	// event handler for brush
	public void paintBrush2(MotionEvent motionEvent) {

		if (this.isBrushOn == 1) {

			int x = (int) motionEvent.getX();
			int y = (int) motionEvent.getY();

			if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
				this.myThread.brush2.touch_start(x, y);

			} else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
				this.myThread.brush2.touch_move(x, y);

			} else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
				this.myThread.brush2.touch_up();

			}
		}
	}

	public void undoBrush() {
		this.brush2.undoBrushStroke();
	}

	public void toggleBrush() {
		Log.d("####", "toggle brush");
		// flip the value of brush
		isBrushOn = isBrushOn ^ 1;
	}

	public int getBrushMode() {
		return isBrushOn;
	}

	public void setResize(boolean bool) {
		isResize = bool;
	}

	public void pause() {
		// tell the thread to stop and waits for it to join
		myThread.setRunning(false);

		boolean retry = true;
		
		isSurfaceDestroyed = true;
		while (retry) {
			try {
				myThread.join();
				retry = false;
			} catch (InterruptedException e) {
				// we will try it again and again...
			}
		}
	}

	public void resume() {

	}

	/********* Controllers *****/

	/**
	 * Move selected layer to front
	 */
	public void moveToFront() {
		int size = drawingLayerArray.size();
		int selectedLayer = myThread.getSelectedLayer();

		Log.d("swap",
				"size is " + size + "    selectedLayer is "
						+ myThread.getSelectedLayer());
		// swap if there are 2 or more items in arrays
		if (size > 1) {
			Log.d("do swapping", "swap layer " + (size - 1) + " with layer "
					+ myThread.getSelectedLayer());
			DrawingLayer toMove = drawingLayerArray.get(selectedLayer);
			// layerArray.set(selectedLayer, layerArray.get(size - 1));
			// layerArray.set(size - 1, toMove);
			Collections.swap(drawingLayerArray, selectedLayer, size - 1);
			myThread.setSelectedLayer(size - 1);
		}
	}

	// add new image and set the thread to see it
	public void addImage(String imagePath) {
		Log.d("imagePath", imagePath);
		drawingLayerArray.add(new DrawingLayer(1, imagePath));
		Log.d("array size", " size is " + drawingLayerArray.size());
		myThread.setSelectedLayer(drawingLayerArray.size() - 1);

	}

	/**
	 * Add a text box with bubble background to canvas
	 * 
	 * @param int style String value
	 */
	public void addBubble(int style, String value) {
		EditText text = new EditText(mContext);
		text.setText(value);
		text.setTextSize(22);

		if (style == 0) {
			text.setBackgroundResource(R.drawable.round_bubble_left);
		} else if (style == 1) {
			text.setBackgroundResource(R.drawable.round_bubble_right);
		} else if (style == 2) {
			text.setBackgroundResource(R.drawable.round_bubble_middle);
		}
		text.setMaxWidth(400);

		text.setDrawingCacheEnabled(true);

		// this is the important code :)
		// Without it the view will have a dimension of 0,0 and the bitmap will
		// be null
		text.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
				MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
		text.layout(0, 0, text.getMeasuredWidth(), text.getMeasuredHeight());

		text.buildDrawingCache(true);
		Bitmap bitmap = Bitmap.createBitmap(text.getDrawingCache());
		text.setDrawingCacheEnabled(false); // clear drawing cache

		drawingLayerArray.add(new DrawingLayer(2, bitmap));
		myThread.setSelectedLayer(drawingLayerArray.size() - 1);
	}

	/**
	 * Add a text with transparent background to canvas
	 * 
	 * @param int style String value
	 */
	public void addText(int style, String value) {
		EditText text = new EditText(mContext);
		text.setText(value);
		text.setBackgroundColor(Color.TRANSPARENT);
		text.setShadowLayer(4, .5f, .5f, Color.GRAY);

		text.setMaxWidth(400); // set the size
		if (style == 0) {
			text.setTextSize(16);
		} else if (style == 1) {
			text.setTextSize(28);
		} else if (style == 2) {
			text.setTypeface(null, Typeface.BOLD);
			text.setTextSize(44);
			text.setMaxWidth(600);
		}

		text.setDrawingCacheEnabled(true);

		// this is the important code :)
		// Without it the view will have a dimension of 0,0 and the bitmap will
		// be null
		text.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
				MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
		text.layout(0, 0, text.getMeasuredWidth(), text.getMeasuredHeight());

		text.buildDrawingCache(true);
		Bitmap bitmap = Bitmap.createBitmap(text.getDrawingCache());
		text.setDrawingCacheEnabled(false); // clear drawing cache

		drawingLayerArray.add(new DrawingLayer(2, bitmap));
		myThread.setSelectedLayer(drawingLayerArray.size() - 1);
	}

	/**
	 * Delete selected layer
	 */
	public void deleteLayer() {
		int arraySize = drawingLayerArray.size();
		if (arraySize == 1) {
			drawingLayerArray.remove(myThread.getSelectedLayer());
		} else if (arraySize > 1) {
			drawingLayerArray.remove(myThread.getSelectedLayer());
			myThread.setSelectedLayer(arraySize - 2); // the next top layer
		}
	}

	/**
	 * move the layer by x amount and y amount in canvas
	 */
	public void moveLayerBy(int layer, int xAmount, int yAmount) {
		DrawingLayer dLayer = drawingLayerArray.get(layer);
		dLayer.setxLoc(myThread.getSelectedLayerXLoc() + xAmount);
		dLayer.setyLoc(myThread.getSelectedLayerYLoc() + yAmount);
	}

	/**
	 * 
	 * @param type
	 */
	public void addDivider(int type) {
		if (type == 0) {
			drawingLayerArray.add(new DrawingLayer(3, BitmapFactory
					.decodeResource(this.getResources(), R.drawable.vertical_line)));
		} else if (type == 1) {
			drawingLayerArray.add(new DrawingLayer(3, BitmapFactory
					.decodeResource(this.getResources(), R.drawable.horizontal_line)));
		}

		myThread.setSelectedLayer(drawingLayerArray.size() - 1);
	}

	/******* Getters and setters *********/

	public boolean isSurfaceDestroyed() {
		return isSurfaceDestroyed;
	}

	public void setSurfaceDestroyed(boolean isSurfaceDestroyed) {
		this.isSurfaceDestroyed = isSurfaceDestroyed;
	}
	
	/**
	 * Fetches the animation thread
	 * 
	 * @return the animation thread
	 */
	public ComicDrawThread getThread() {
		return myThread;
	}

}